<template>
  <transition name="haloe-scrollbar-fade">
    <div
      v-show="always || visible"
      ref="instance"
      :class="outerClasses"
      @mousedown="clickTrackHandler"
    >
      <div
        ref="thumb"
        :class="innerClasses"
        :style="thumbStyle"
        @mousedown="clickThumbHandler"
      ></div>
    </div>
  </transition>
</template>

<script setup>
import { ref,computed, onBeforeUnmount,toRef,inject } from 'vue'
import { useEventListener } from '@vueuse/core'

import { BAR_MAP, renderThumbStyle } from './util'
import {isClient} from "../../utils/index.js"


const prefixCls = 'haloe-scrollbar'
const scrollbar = inject('scrollbarContextKey')

if (!scrollbar) console.error('Thumb can not inject scrollbar context')

const props = defineProps({
  vertical: Boolean,
  size: String,
  move: Number,
  ratio: {
    type: Number,
    required: true,
  },
  always: Boolean,
})

const instance = ref()
const thumb = ref()

const thumbState = ref({})
const visible = ref(false)

let cursorDown = false
let cursorLeave = false
let originalOnSelectStart = isClient ? document.onselectstart : null

const bar = computed(() => BAR_MAP[props.vertical ? 'vertical' : 'horizontal'])
const outerClasses = computed(() => {
  return [prefixCls + '-bar', `is-` + bar.value.key]
})

const innerClasses = computed(() => {
  return prefixCls + '-thumb'
})

const thumbStyle = computed(() =>
  renderThumbStyle({
    size: props.size,
    move: props.move,
    bar: bar.value,
  })
)

const offsetRatio = computed(
  () =>
    // offsetRatioX = original width of thumb / current width of thumb / ratioX
    // offsetRatioY = original height of thumb / current height of thumb / ratioY
    // instance height = wrap height - GAP
    instance.value[bar.value.offset] ** 2 /
    scrollbar.wrapElement[bar.value.scrollSize] /
    props.ratio /
    thumb.value[bar.value.offset]
)

const startDrag = (e) => {
  e.stopImmediatePropagation()
  cursorDown = true
  document.addEventListener('mousemove', mouseMoveDocumentHandler)
  document.addEventListener('mouseup', mouseUpDocumentHandler)
  originalOnSelectStart = document.onselectstart
  document.onselectstart = () => false
}

const clickThumbHandler = (e) => {
  e.stopPropagation()
  if (e.ctrlKey || [1, 2].includes(e.button)) return
  if(window.getSelection())window.getSelection().removeAllRanges()

  startDrag(e)
  const el = e.currentTarget
  if (!el) return
  thumbState.value[bar.value.axis] =
    el[bar.value.offset] -
    (e[bar.value.client] - el.getBoundingClientRect()[bar.value.direction])
}

const clickTrackHandler = (e) => {
  if (!thumb.value || !instance.value || !scrollbar.wrapElement) return

  const offset = Math.abs(
    e.target.getBoundingClientRect()[bar.value.direction] - e[bar.value.client]
  )
  const thumbHalf = thumb.value[bar.value.offset] / 2
  const thumbPositionPercentage =
    ((offset - thumbHalf) * 100 * offsetRatio.value) /
    instance.value[bar.value.offset]

  scrollbar.wrapElement[bar.value.scroll] =
    (thumbPositionPercentage * scrollbar.wrapElement[bar.value.scrollSize]) /
    100
}

const mouseMoveDocumentHandler = (e) => {
  if (!instance.value || !thumb.value) return
  if (cursorDown === false) return

  const prevPage = thumbState.value[bar.value.axis]
  if (!prevPage) return

  const offset =
    (instance.value.getBoundingClientRect()[bar.value.direction] -
      e[bar.value.client]) *
    -1
  const thumbClickPosition = thumb.value[bar.value.offset] - prevPage
  const thumbPositionPercentage =
    ((offset - thumbClickPosition) * 100 * offsetRatio.value) /
    instance.value[bar.value.offset]
  scrollbar.wrapElement[bar.value.scroll] =
    (thumbPositionPercentage * scrollbar.wrapElement[bar.value.scrollSize]) /
    100
}

const mouseUpDocumentHandler = () => {
  cursorDown = false
  thumbState.value[bar.value.axis] = 0
  document.removeEventListener('mousemove', mouseMoveDocumentHandler)
  document.removeEventListener('mouseup', mouseUpDocumentHandler)
  restoreOnselectstart()
  if (cursorLeave) visible.value = false
}

const mouseMoveScrollbarHandler = () => {
  cursorLeave = false
  visible.value = !!props.size
}

const mouseLeaveScrollbarHandler = () => {
  cursorLeave = true
  visible.value = cursorDown
}

onBeforeUnmount(() => {
  restoreOnselectstart()
  document.removeEventListener('mouseup', mouseUpDocumentHandler)
})

const restoreOnselectstart = () => {
  if (document.onselectstart !== originalOnSelectStart)
    document.onselectstart = originalOnSelectStart
}

useEventListener(
  toRef(scrollbar, 'scrollbarElement'),
  'mousemove',
  mouseMoveScrollbarHandler
)
useEventListener(
  toRef(scrollbar, 'scrollbarElement'),
  'mouseleave',
  mouseLeaveScrollbarHandler
)

</script>

<style></style>
